// Filename: inputclass.cpp

#include "inputclass.h"

//---------------------------------------------------------------------------------------------------------------
InputClass::InputClass()
	: m_directInput(0), m_keyboard(0), m_mouse(0)
{ }

//---------------------------------------------------------------------------------------------------------------
InputClass::InputClass(const InputClass& rhs)
{ }

//---------------------------------------------------------------------------------------------------------------
InputClass::~InputClass()
{ }

//---------------------------------------------------------------------------------------------------------------
bool InputClass::Initialize(HINSTANCE hinstance, HWND hwnd, int screenWidth, int screenHeight)
{
	HRESULT result;

	// Store the screen size which will be used for positioning the mouse cursor.
	m_screenWidth  = screenWidth;
	m_screenHeight = screenHeight;

	// Initialize the location of the mouse on the screen.
	m_mouseX = 0;
	m_mouseY = 0;

	// Initialize the main direct input interface.
	result = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_directInput, NULL);
	if (FAILED(result))
		return false;

	// Initialize the direct input interface for the keyboard.
	result = m_directInput->CreateDevice(GUID_SysKeyboard, &m_keyboard, NULL);
	if (FAILED(result))
		return false;

	// Set the data format. In this case since it is a keyboard we can use the predefined data format.
	result = m_keyboard->SetDataFormat(&c_dfDIKeyboard);
	if (FAILED(result))
		return false;

	// Set the cooperative level of the keyboard to not share with other programs.
	result = m_keyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE);
	if (FAILED(result))
		return false;

	// Now acquire the keyboard.
	result = m_keyboard->Acquire();
	if (FAILED(result))
		return false;

	// Set up initial keyboard states so current and previous states are the same.
	m_keyboard->GetDeviceState(sizeof(m_keyState), (LPVOID)&m_keyState);
	memcpy(m_previousKeyState, m_keyState, sizeof(m_keyState));

	// Initialize the direct input interface for the mouse.
	result = m_directInput->CreateDevice(GUID_SysMouse, &m_mouse, NULL);
	if (FAILED(result))
		return false;

	// Set the data format for the mouse using the pre-defined mouse data format.
	result = m_mouse->SetDataFormat(&c_dfDIMouse);
	if (FAILED(result))
		return false;

	// Set the cooperative level of the mouse to share with other programs.
	result = m_mouse->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if (FAILED(result))
		return false;

	// Acquire the mouse.
	result = m_mouse->Acquire();
	if (FAILED(result))
		return false;

	return true;
}

//---------------------------------------------------------------------------------------------------------------
void InputClass::Shutdown()
{
	// Release the mouse.
	if (m_mouse)
	{
		m_mouse->Unacquire();
		m_mouse->Release();
		m_mouse = nullptr;
	}

	// Release the keyboard.
	if (m_keyboard)
	{
		m_keyboard->Unacquire();
		m_keyboard->Release();
		m_keyboard = nullptr;
	}

	// Release the main interface to direct input.
	if (m_directInput)
	{
		m_directInput->Release();
		m_directInput = nullptr;
	}
}

//---------------------------------------------------------------------------------------------------------------
bool InputClass::Frame()
{
	bool result;

	// Read the current state of the keyboard.
	result = ReadKeyboard();
	if (!result)
		return false;

	// Read the current state of the mouse.
	result = ReadMouse();
	if (!result)
		return false;

	// Process the changes in the mouse and keyboard.
	ProcessInput();

	return true;
}

//---------------------------------------------------------------------------------------------------------------
bool InputClass::ReadKeyboard()
{
	HRESULT result;

	// Read the keyboard device.
	result = m_keyboard->GetDeviceState(sizeof(m_keyState), (LPVOID)&m_keyState);
	if (FAILED(result))
	{
		// If the keyboard lost focus or was not acquired then try to get control back.
		if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
		{
			m_keyboard->Acquire();
		}
		else
		{
			return false;
		}
	}

	return true;
}

//---------------------------------------------------------------------------------------------------------------
bool InputClass::ReadMouse()
{
	HRESULT result;

	// Read the mouse device.
	result = m_mouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&m_mouseState);
	if (FAILED(result))
	{
		// If the mouse lost focus or was not acquired then try to get control back.
		if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
		{
			m_mouse->Acquire();
		}
		else
		{
			return false;
		}
	}

	return true;
}

//---------------------------------------------------------------------------------------------------------------
void InputClass::ProcessInput()
{
	// Update the location of the mouse cursor based on the change of the mouse location during the frame.
	m_mouseX += m_mouseState.lX;
	m_mouseY += m_mouseState.lY;

	// Ensure the mouse location doesn't exceed the screen width or height.
	if (m_mouseX < 0) { m_mouseX = 0; }
	if (m_mouseY < 0) { m_mouseY = 0; }

	if (m_mouseX > m_screenWidth)  { m_mouseX = m_screenWidth;  }
	if (m_mouseY > m_screenHeight) { m_mouseY = m_screenHeight; }
}

//---------------------------------------------------------------------------------------------------------------
bool InputClass::IsEscapePressed()
{
	// Do a bitwise and on the keyboard state to check if the escape key is currently being pressed.
	if (m_keyState[DIK_ESCAPE] & 0x80)
	{
		return true;
	}

	return false;
}

//---------------------------------------------------------------------------------------------------------------
void InputClass::GetMouseLocation(int& mouseX, int& mouseY)
{
	mouseX = m_mouseX;
	mouseY = m_mouseY;
}

//---------------------------------------------------------------------------------------------------------------
void InputClass::IsKeyPressed(vector<char>& vec, string& command)
{
	// Firstly check if the return or backspace key is pressed.
	if (KEYDOWN(m_previousKeyState, DIK_RETURN) && !KEYDOWN(m_keyState, DIK_RETURN))
	{
		// TODO: Process possible command (in graphics class)
		for (UINT i = 0; i < vec.size(); ++i)
			command.push_back(vec[i]);

		vec.clear();				// Return (Erase)
	}
	else if (KEYDOWN(m_previousKeyState, DIK_BACKSPACE) && !KEYDOWN(m_keyState, DIK_BACKSPACE))
	{	
		if (vec.size() > 0)
			vec.pop_back();			// Backspace
	}

	// Return if we can't add any more characters.
	if (vec.size() > 127)
	{
		// Copy current keyboard state in previous keyboard state.
		memcpy(m_previousKeyState, m_keyState, sizeof(m_keyState));
		return;
	}

	if		(KEYDOWN(m_previousKeyState, DIK_A) && !KEYDOWN(m_keyState, DIK_A))				vec.push_back('a');
	else if (KEYDOWN(m_previousKeyState, DIK_B) && !KEYDOWN(m_keyState, DIK_B))				vec.push_back('b');
	else if (KEYDOWN(m_previousKeyState, DIK_C) && !KEYDOWN(m_keyState, DIK_C))				vec.push_back('c');
	else if (KEYDOWN(m_previousKeyState, DIK_D) && !KEYDOWN(m_keyState, DIK_D))				vec.push_back('d');
	else if (KEYDOWN(m_previousKeyState, DIK_E) && !KEYDOWN(m_keyState, DIK_E))				vec.push_back('e');
	else if (KEYDOWN(m_previousKeyState, DIK_F) && !KEYDOWN(m_keyState, DIK_F))				vec.push_back('f');
	else if (KEYDOWN(m_previousKeyState, DIK_G) && !KEYDOWN(m_keyState, DIK_G))				vec.push_back('g');
	else if (KEYDOWN(m_previousKeyState, DIK_H) && !KEYDOWN(m_keyState, DIK_H))				vec.push_back('h');
	else if (KEYDOWN(m_previousKeyState, DIK_I) && !KEYDOWN(m_keyState, DIK_I))				vec.push_back('i');
	else if (KEYDOWN(m_previousKeyState, DIK_J) && !KEYDOWN(m_keyState, DIK_J))				vec.push_back('j');
	else if (KEYDOWN(m_previousKeyState, DIK_K) && !KEYDOWN(m_keyState, DIK_K))				vec.push_back('k');
	else if (KEYDOWN(m_previousKeyState, DIK_L) && !KEYDOWN(m_keyState, DIK_L))				vec.push_back('l');
	else if (KEYDOWN(m_previousKeyState, DIK_M) && !KEYDOWN(m_keyState, DIK_M))				vec.push_back('m');
	else if (KEYDOWN(m_previousKeyState, DIK_N) && !KEYDOWN(m_keyState, DIK_N))				vec.push_back('n');
	else if (KEYDOWN(m_previousKeyState, DIK_O) && !KEYDOWN(m_keyState, DIK_O))				vec.push_back('o');
	else if (KEYDOWN(m_previousKeyState, DIK_P) && !KEYDOWN(m_keyState, DIK_P))				vec.push_back('p');
	else if (KEYDOWN(m_previousKeyState, DIK_Q) && !KEYDOWN(m_keyState, DIK_Q))				vec.push_back('q');
	else if (KEYDOWN(m_previousKeyState, DIK_R) && !KEYDOWN(m_keyState, DIK_R))				vec.push_back('r');
	else if (KEYDOWN(m_previousKeyState, DIK_S) && !KEYDOWN(m_keyState, DIK_S))				vec.push_back('s');
	else if (KEYDOWN(m_previousKeyState, DIK_T) && !KEYDOWN(m_keyState, DIK_T))				vec.push_back('t');
	else if (KEYDOWN(m_previousKeyState, DIK_U) && !KEYDOWN(m_keyState, DIK_U))				vec.push_back('u');
	else if (KEYDOWN(m_previousKeyState, DIK_V) && !KEYDOWN(m_keyState, DIK_V))				vec.push_back('v');
	else if (KEYDOWN(m_previousKeyState, DIK_W) && !KEYDOWN(m_keyState, DIK_W))				vec.push_back('w');
	else if (KEYDOWN(m_previousKeyState, DIK_X) && !KEYDOWN(m_keyState, DIK_X))				vec.push_back('x');
	else if (KEYDOWN(m_previousKeyState, DIK_Y) && !KEYDOWN(m_keyState, DIK_Y))				vec.push_back('y');
	else if (KEYDOWN(m_previousKeyState, DIK_Z) && !KEYDOWN(m_keyState, DIK_Z))				vec.push_back('z');
	else if (KEYDOWN(m_previousKeyState, DIK_SPACE) && !KEYDOWN(m_keyState,  DIK_SPACE))	vec.push_back(' ');		// Space

	// Copy current keyboard state in previous keyboard state.
	memcpy(m_previousKeyState, m_keyState, sizeof(m_keyState));
}